#include "helper.h"

#include "../include/thread.h"

namespace mix
{

struct ThreadImpl
{
#ifdef M_WIN
    HANDLE thread;
#else
    pthread_t thread;
#endif

    ThreadImpl() : thread( 0) {}
};

#define _THREADIMPL ((ThreadImpl*)_impl)

Thread::Thread( const std::string& name)
    : _state( New), _name( name){
    _impl = new ThreadImpl();
}

Thread::Thread( const ThreadFunc& func, const std::string& name)
    : _state( New), _threadFunc( func), _name( name){
    _impl = new ThreadImpl();
}

Thread::~Thread() {
    if ( _state != New)
        join();
    if ( _impl) {
        ThreadImpl* ti = _THREADIMPL;
        delete ti;
        _impl = NULL;
    }
}

bool Thread::setThreadFunc( const ThreadFunc& func) {
    ThreadImpl* ti = _THREADIMPL;
    if ( ti->thread)
        return false;
    _threadFunc = func;
    return true;
}

void callThreadRun( Thread* t) {
    t->run();
}

#ifdef M_WIN
DWORD __stdcall threadProc( void* param)
#else
void* threadProc( void* param)
#endif
{
    Thread* thread = (Thread*)param;
    callThreadRun( thread); 
    return 0;
}


bool Thread::start() {
    ThreadImpl* ti = _THREADIMPL;
    if ( ti->thread)
        return false;
#ifdef M_WIN
    DWORD id;
    ti->thread = ::CreateThread( NULL, 0, threadProc, this, 0, &id);
    if ( ti->thread)
        return true;
    else
        return false;
#else
    if ( ::pthread_create( &(ti->thread), NULL, threadProc, this))
        return false;
    else
        return true;
#endif
}

void Thread::run() {
    _state = Running;
    if ( _threadFunc != NULL)
        _threadFunc();
    _state = Terminated;
}

bool Thread::join() {
    ThreadImpl* ti = _THREADIMPL;
    if ( !ti->thread)
        return false;
#ifdef M_WIN
    if ( ::WaitForSingleObject( ti->thread, INFINITE) == WAIT_FAILED)
        return false;
    ::CloseHandle( ti->thread);
    ti->thread = 0;
#else
    if ( pthread_join( ti->thread, NULL))
        return false;
    ti->thread = 0;
#endif
    return true;
}

bool Thread::detach() {
    ThreadImpl* ti = _THREADIMPL;
#ifdef M_WIN
#else
    if ( !ti->thread)
        return false;
    if ( pthread_detach( ti->thread))
        return false;
    ti->thread = 0;
#endif
    return true;
}

void* Thread::getNative() {
    ThreadImpl* ti = _THREADIMPL;
    return &(ti->thread);
}

void Thread::exit( int code) {
#ifdef M_WIN
    ExitThread( *(DWORD*)&code); 
#else
    pthread_exit( &code);
#endif
}

void Thread::yield() {
#ifdef M_WIN
    ::Sleep( 0);
#else
    ::pthread_yield();
#endif
}

void Thread::sleep( long msec) {
#ifdef M_WIN
    ::Sleep( msec);
#else
    struct timespec req, rem;
    req.tv_sec = msec / 1000;
    req.tv_nsec = msec % 1000 * 1000 * 1000;
    while ( ::nanosleep( &req, &rem) != 0) {
        if ( errno != EINTR)
            return;
        req = rem;
    }
#endif
}


}   // namespace mix
